<?php

namespace Sbing\AliPaySocialite;

use Illuminate\Support\Arr;
use Laravel\Socialite\Two\ProviderInterface;
use SocialiteProviders\Manager\OAuth2\AbstractProvider;
use SocialiteProviders\Manager\OAuth2\User;

class Provider extends AbstractProvider implements ProviderInterface
{
    /**
     * Unique Provider Identifier.
     */
    const IDENTIFIER = 'ALIPAY';

    /**
     * The scopes being requested.
     *
     * @var array
     */
    protected $scopes = ['auth_user'];

    /**
     * {@inheritdoc}.
     */
    protected function getAuthUrl($state)
    {
        return $this->buildAuthUrlFromBase('https://openauth.alipay.com/oauth2/publicAppAuthorize.htm', $state);
    }

    /**
     * {@inheritdoc}.
     */
    protected function getCodeFields($state = null)
    {
        $fields = [
            'app_id' => $this->clientId,
            'redirect_uri' => $this->redirectUrl,
            'scope' => $this->formatScopes($this->getScopes(), $this->scopeSeparator),
        ];

        if ($this->usesState()) {
            $fields['state'] = $state;
        }

        return array_merge($fields, $this->parameters);
    }

    /**
     * {@inheritdoc}.
     */
    protected function getTokenUrl()
    {
        return 'https://openapi.alipay.com/gateway.do';
    }

    /**
     * {@inheritdoc}.
     */
    protected function mapUserToObject(array $user)
    {
        return (new User())->setRaw($user)->map([
            'id' => $user['user_id'],
            'nickname' => $user['nick_name'],
            'avatar' => $user['avatar'],
            'name' => null,
            'email' => null,
        ]);
    }

    /**
     * {@inheritdoc}.
     */
    protected function getTokenFields($code)
    {
        $fields = [
            'app_id' => $this->clientId,
            'method' => 'alipay.system.oauth.token',
            'charset' => 'utf-8',
            'sign_type' => 'RSA2',
            'timestamp' => date('Y-m-d H:i:s'),
            'version' => '1.0',
            'grant_type' => 'authorization_code',
            'code' => $code,
        ];

        return $this->sign($fields);
    }

    /**
     * Get the code from the request.
     *
     * @return string
     */
    protected function getCode()
    {
        return $this->request->input('auth_code');
    }

    /**
     * {@inheritdoc}.
     */
    protected function getUserByToken($token)
    {
        $fields = [
            'app_id' => $this->clientId,
            'method' => 'alipay.user.info.share',
            'charset' => 'utf-8',
            'sign_type' => 'RSA2',
            'timestamp' => date('Y-m-d H:i:s'),
            'version' => '1.0',
            'grant_type' => 'authorization_code',
            'auth_token' => $token,
        ];

        $response = $this->getHttpClient()->get($this->getTokenUrl(), [
            'query' => $this->sign($fields),
        ]);

        $arr = json_decode($response->getBody()->getContents(), true);

        return Arr::get($arr, 'alipay_user_info_share_response');
    }

    /**
     * Get the access token from the token response body.
     *
     * @param  string  $body
     *
     * @return string
     */
    protected function parseAccessToken($body)
    {
        return Arr::get($body, 'alipay_system_oauth_token_response.access_token');
    }

    /**
     * 将参数签名
     *
     * @param  array  $fields
     *
     * @return array
     */
    protected function sign(array $fields)
    {
        ksort($fields);

        openssl_sign(urldecode(http_build_query($fields)), $sign, openssl_get_privatekey(file_get_contents(config('services.alipay.client_secret'))), 'sha256');

        $fields['sign'] = base64_encode($sign);

        return $fields;
    }
}
